﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;

using dotUML.Core.Common;
using dotUML.Core.Common.SequenceDiagram;
using dotUML.System;

namespace dotUML.Core.Parser.SequenceDiagram
{
    /// <summary>
    /// Sequence diagram parser class.
    /// </summary>
    class SequenceDiagramXmlParser : XmlParserBase, ISequenceDiagramXmlParser
    {
        /// <summary>
        /// Class constructor.
        /// </summary>
        public SequenceDiagramXmlParser()
        {
            ElementParserList = new Dictionary<string, ISequenceDiagramElementParser>();

            InstantiateElementParsers();
        }

        /// <summary>
        /// Instantiate all element parser objects.
        /// </summary>
        private void InstantiateElementParsers()
        {
            ElementParserList.Add(Constants.SequenceDiagram.LifelinesNode, (ISequenceDiagramElementParser)InstrumentationManager.InstrumentObject(new LifelinesParser(), EInstrumentationType.Logging));
            ElementParserList.Add(Constants.SequenceDiagram.MessagesNode, (ISequenceDiagramElementParser)InstrumentationManager.InstrumentObject(new MessagesParser(), EInstrumentationType.Logging));
            ElementParserList.Add(Constants.SequenceDiagram.ConstraintsNode, (ISequenceDiagramElementParser)InstrumentationManager.InstrumentObject(new ConstraintsParser(), EInstrumentationType.Logging));
            ElementParserList.Add(Constants.SequenceDiagram.CombinedFragmentsNode, (ISequenceDiagramElementParser)InstrumentationManager.InstrumentObject(new CombinedFragmentsParser(), EInstrumentationType.Logging));
            ElementParserList.Add(Constants.SequenceDiagram.InvariantsNode, (ISequenceDiagramElementParser)InstrumentationManager.InstrumentObject(new InvariantsParser(), EInstrumentationType.Logging));
            ElementParserList.Add(Constants.SequenceDiagram.DestructionsNode, (ISequenceDiagramElementParser)InstrumentationManager.InstrumentObject(new DestructionsParser(), EInstrumentationType.Logging));
            ElementParserList.Add(Constants.SequenceDiagram.ContinuationsNode, (ISequenceDiagramElementParser)InstrumentationManager.InstrumentObject(new ContinuationsParser(), EInstrumentationType.Logging));
            ElementParserList.Add(Constants.SequenceDiagram.ConcurrentsNode, (ISequenceDiagramElementParser)InstrumentationManager.InstrumentObject(new ConcurrentsParser(), EInstrumentationType.Logging));
            ElementParserList.Add(Constants.SequenceDiagram.ReferencesNode, (ISequenceDiagramElementParser)InstrumentationManager.InstrumentObject(new ReferencesParser(), EInstrumentationType.Logging));
            ElementParserList.Add(Constants.SequenceDiagram.SequenceNode, (ISequenceDiagramElementParser)InstrumentationManager.InstrumentObject(new SequencesParser(), EInstrumentationType.Logging));
        }

        /// <summary>
        /// Assign sequence diagram name in element parser objects.
        /// </summary>
        /// <param name="DiagramName">Sequence diagram name.</param>
        private void SetSequenceDiagramName(string DiagramName)
        {
            foreach(string key in ElementParserList.Keys)
            {
                ISequenceDiagramElementParser parser = ElementParserList[key];
                parser.DiagramName = DiagramName;
            }
        }

        #region ISequenceDiagramXmlParser Members

        Dictionary<string, ISequenceDiagramElementParser> _elementParserList;

        /// <summary>
        /// Sequence diagram element parsers.
        /// </summary>
        public Dictionary<string, ISequenceDiagramElementParser> ElementParserList
        {
            get
            {
                return _elementParserList;
            }
            set
            {
                _elementParserList = value;
            }
        }

        #endregion

        /// <summary>
        /// Parse a sequence diagram xml block. And create a sequence
        /// diagram in a dotUML model.
        /// </summary>
        /// <param name="Xml">Xml block.</param>
        /// <param name="Model">dotUML model.</param>
        /// <returns>True if parsing was successful, otherwise - false.</returns>
        public override bool Parse(string Xml, dotUML.Core.Common.IModel Model)
        {
            bool result = true;
            string sequenceDiagramName;

            this.Model = Model;

            try
            {
                XElement sequenceDiagramElement = XElement.Parse(Xml);
                sequenceDiagramName = sequenceDiagramElement.Attribute(Constants.Common.NameAttribute).Value;

                ISequenceDiagram sequenceDiagram = Model.AddSequenceDiagram(sequenceDiagramName);
                SetSequenceDiagramName(sequenceDiagramName);

                IEnumerable<XElement> elements = sequenceDiagramElement.Elements(); 
                foreach (XElement element in elements)
                {
                    if (!ElementParserList.ContainsKey(element.Name.LocalName))
                    {
                        throw new ParserException(string.Format(Resources.SequenceDiagramXmlParser_Parse_UnknownElement, element.Name));
                    }

                    ISequenceDiagramElementParser parser = ElementParserList[element.Name.LocalName];
                    result = parser.Parse(element.ToString(), Model);

                    if (!result)
                    {
                        Errors.AddRange(parser.Errors);
                        break;
                    }
                }

            }
            catch (Exception ex)
            {
                result = false;
                NewError(EErrorType.Error, string.Format(Resources.SequenceDiagramXmlParser_Parse_Error, ex.Message));
                // TODO: Log exception
            }

            return result;
        }

    }
}
